package roborally;

import java.util.ArrayList;
import java.util.List;

import command.Command;
import command.If;
import command.Move;
import command.PickupAndUse;
import command.Sequence;
import command.Shoot;
import command.Turn;
import command.While;
import condition.And;
import condition.AtItem;
import condition.CanHitRobot;
import condition.Condition;
import condition.EnergyAtLeast;
import condition.Not;
import condition.Or;
import condition.SubRange;
import condition.True;
import condition.WallCondition;
/**
 * A class representing the compiler of a string to an entry.
 * @version	3.0
 * @author 	Lies Tambeur 	r0264469	2Bcwsbbh
 * @author	Eline Vanrykel	r0264543	2Bcwselt 
 */
public class Compiler {
		/**
		 * Returns the corresponding entry of the given string.
		 * @pre	the energy is in Ws.
		 * @param 	string
		 * 			The string to be converted.
		 * @return	The corresponding entry of the given string.
		 */
		public static Entry compile(String string){
			string = string.replaceAll(" ","");  //verwijdert alle spaties
			string = string.replaceAll("	","");  //verwijdert alle tabs
			if(string.startsWith("(")){
				if(string.startsWith("(move")){
					return new Move();
				}
				else if(string.startsWith("(shoot")){
					return new Shoot();
				}
				else if(string.startsWith("(pickup-and-use")){
					return new PickupAndUse();
				}
				else if(string.startsWith("(turn")){
					string = string.substring(5).trim();
					if(string.startsWith("clockwise")){
						return new Turn(Direction.CLOCKWISE);
					}
					else if(string.startsWith("counterclockwise")){
						return new Turn(Direction.COUNTERCLOCKWISE);
					}
				}
				//basicconditions
				else if(string.startsWith("(wall")){
					return new WallCondition();
				}
				else if(string.startsWith("(at-item")){
					return new AtItem();
				}
				else if(string.startsWith("(can-hit-robot")){
					return new CanHitRobot();
				}
				else if(string.startsWith("(energy-at-least")){
					string =string.substring(16);
					int index = string.indexOf(")");
					string = string.substring(0,index);
					double value = Double.parseDouble(string);
					return new EnergyAtLeast(new EnergyAmount(value,EnergyUnit.WATTSECONDS));
				}
				else if(string.startsWith("(true")){
					return new True();
				}
				else if(string.startsWith("(between")){ //syntax= (between (3,4) (6,7) )
					string = string.substring(8);
					int first = string.indexOf(",");
					int second = string.indexOf(")");
					int third = string.substring(second+1, string.length()).indexOf(",")+second+1;
					long valuex1 = Long.parseLong(string.substring(1,first));
					long valuey1 = Long.parseLong(string.substring(first+1,second));
					long valuex2 = Long.parseLong(string.substring(second+2,third));
					long valuey2 = Long.parseLong(string.substring(third+1,string.length()-2));
					return new SubRange(new Position(valuex1,valuey1), new Position(valuex2,valuey2));
				}
				// combined
				else if(string.startsWith("(and")){
					int closingIndex = indexOfClosing(string) -4 ;  //-4 = -(and
					string = string.substring(4);  

					int index =  indexOfClosing(string);
					String subone = string.substring(0,index+1);
					string = string.substring(index+1);
					closingIndex = closingIndex - (index + 1);
					Condition result = (Condition) compile(subone);					
					while(closingIndex!=0){
						index = indexOfClosing(string);
						result = new And(result,(Condition) compile(string.substring(0,index+1)));
						string = string.substring(index+1);
						closingIndex = closingIndex - (index + 1);
					}
					return result;
				}
				else if(string.startsWith("(or")){
					int closingIndex = indexOfClosing(string) -3 ;  //-3 = -(or
					string = string.substring(3);  

					int index =  indexOfClosing(string);
					String subone = string.substring(0,index+1);
					string = string.substring(index+1);
					closingIndex = closingIndex - (index + 1);
					Condition result = (Condition) compile(subone);					
					while(closingIndex!=0){
						index = indexOfClosing(string);
						result = new Or(result,(Condition) compile(string.substring(0,index+1)));
						string = string.substring(index+1);
						closingIndex = closingIndex - (index + 1);
					}
					return result;
				}
				else if(string.startsWith("(not")){
					int closingIndex = indexOfClosing(string) -4 ;  //-4 = -(not
					string = string.substring(4); 
					
					int index =  indexOfClosing(string);
					String subone = string.substring(0,index+1);
					Condition result = (Condition) compile(subone);					
					result = new Not(result);
					string = string.substring(index+1);
					closingIndex = closingIndex - (index + 1);
					return result;
				}
				else if(string.startsWith("(while")){
					string = string.substring(6);
					int index = indexOfClosing(string);
					String cond = string.substring(0,index+1);
					string = string.substring(index+1);
					index = indexOfClosing(string);
					String comm = string.substring(0, index+1);
					return new While((Condition) compile(cond), (Command) compile(comm));
				}
				else if(string.startsWith("(if")){
					string = string.substring(3);
					int index = indexOfClosing(string);
					String cond = string.substring(0,index+1);
					string = string.substring(index+1);
					index = indexOfClosing(string);
					String comm = string.substring(0, index+1);
					string = string.substring(index+1);
					index = indexOfClosing(string);
					String elsecomm = string.substring(0, index+1);
					return new If((Condition) compile(cond), (Command) compile(comm), (Command) compile(elsecomm));
				}
				else if(string.startsWith("(seq")){
					int closingIndex = indexOfClosing(string) -4 ;  //-4 = -(seq
					string = string.substring(4);  
					List<Command> commands = new ArrayList<Command>();			
					while(closingIndex!=0){
						int index = indexOfClosing(string);
						commands.add((Command) compile(string.substring(0,index+1)));
						string = string.substring(index+1);
						closingIndex = closingIndex - (index + 1);
					}
					return new Sequence(commands);
				}
			}
			else{
				throw new IllegalArgumentException();
			}
			return null;
		}
		
		/**
		 * Returns the index of the closing bracket.
		 * @param 	string
		 * 			The string to be examined.
		 * @return	The index of the closing bracket.
		 */
		private static int indexOfClosing(String string){
			if(string.startsWith("(")){
				int open = 1;
				int closed = 0;
				int index = 0;
				while(open!=closed){
					string = string.substring(1);
					index = index +1;
					if(string.startsWith("(")){
						open = open + 1;
					}
					else if(string.startsWith(")")){
						closed = closed +1 ;
					}
				}
				return index;
			}
			return -1;
		}
}
